/*
author:杨文�?
email:yangwenchao@keynection.cn
*/
#pragma once

#include <inttypes.h>                       // needs to be included for default system types (such as PRIxx)
#include "driver/uart.h"                    // for UART types
#include "sdkconfig.h"
#include "geminai_log.h"

#ifdef __cplusplus
extern "C" {
#endif

#if __has_include("esp_check.h")
#include "esp_check.h"
#include "esp_log.h"

#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) ESP_RETURN_ON_FALSE(a, err_code, tag, format __VA_OPT__(,) __VA_ARGS__)

#else

// if cannot include esp_check then use custom check macro

#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) do {                              \
    if (!(a)) {                                                                             \
        GEMINAI_LOGE(tag, "%s(%" PRIu32 "): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \
        return err_code;                                                                    \
    }                                                                                       \
} while(0)

#endif

#define MB_CONTROLLER_STACK_SIZE            (CONFIG_GMB_CONTROLLER_STACK_SIZE)   // Stack size for Modbus controller
#define MB_CONTROLLER_PRIORITY              (CONFIG_GMB_PORT_TASK_PRIO - 1)    // priority of MB controller task

// Default port defines
#define MB_DEVICE_ADDRESS   (1)                 // Default slave device address in Modbus
#define MB_DEVICE_SPEED     (230400)            // Default Modbus speed for now hard defined
#define MB_UART_PORT        (UART_NUM_MAX - 1)  // Default UART port number
#define MB_PAR_INFO_TOUT    (10)                // Timeout for get parameter info
#define MB_PARITY_NONE      (UART_PARITY_DISABLE)

// The Macros below handle the endianness while transfer N byte data into buffer (convert from network byte order)
#define _XFER_2_RD(dst, src) { \
    *(uint8_t *)(dst)++ = *(uint8_t *)(src + 1); \
    *(uint8_t *)(dst)++ = *(uint8_t *)(src + 0); \
    (src) += 2; \
}

#define _XFER_2_WR(dst, src) { \
    *(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
    *(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
}


/*
Event group for parameters notification
*/
typedef enum
{
    MB_EVENT_NO_EVENTS = 0x00,
    MB_EVENT_HOLDING_REG_WR = BIT0,         /* Modbus Event Write Holding registers. */
    MB_EVENT_HOLDING_REG_RD = BIT1,         /* Modbus Event Read Holding registers. */
    MB_EVENT_INPUT_REG_RD = BIT3,           /* Modbus Event Read Input registers. */
    MB_EVENT_COILS_WR = BIT4,               /* Modbus Event Write Coils. */
    MB_EVENT_COILS_RD = BIT5,               /* Modbus Event Read Coils. */
    MB_EVENT_DISCRETE_RD = BIT6,            /* Modbus Event Read Discrete bits. */
    MB_EVENT_STACK_STARTED = BIT7           /* Modbus Event Stack started */
} mb_event_group_t;

/*
Type of Modbus parameter
*/
typedef enum {
    MB_PARAM_HOLDING = 0x00,         /* Modbus Holding register. */
    MB_PARAM_INPUT,                  /* Modbus Input register. */
    MB_PARAM_COIL,                   /* Modbus Coils. */
    MB_PARAM_DISCRETE,               /* Modbus Discrete bits. */
    MB_PARAM_COUNT,
    MB_PARAM_UNKNOWN = 0xFF
} mb_param_type_t;

/*
Serial communication structure to setup Modbus controller
*/
typedef struct {
	uint8_t slave_addr;                     /* Modbus slave address field (dummy for master) */
    uart_port_t port;                       /* Modbus communication port (UART) number */
    uint32_t baudrate;                      /* Modbus baudrate */
    uart_parity_t parity;                   /* Modbus UART parity settings */
    uint16_t dummy_port;                    /* Dummy field, unused */
} mb_communication_info_t;

/*
common interface method types
*/
typedef esp_err_t (*iface_init)(void**);        /* Interface method init */
typedef esp_err_t (*iface_destroy)(void);       /* Interface method destroy */
typedef esp_err_t (*iface_setup)(void*);        /* Interface method setup */
typedef esp_err_t (*iface_start)(void);         /* Interface method start */

#ifdef __cplusplus
}
#endif

